package top.bulk.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import top.bulk.entity.Order;
import top.bulk.entity.User;
import top.bulk.service.OrderService;
import top.bulk.service.UserService;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * TxFailureComponent
 * 演示事务失效
 *
 * @author 散装java
 * @version 1.0.0
 * @date 2025-02-16
 */
@Component
@Slf4j
public class TxFailureComponent {
    @Resource
    private UserService userService;
    @Resource
    private OrderService orderService;

    @Resource
    private TxFailureComponent self;

    /**
     * 测试两个数据源情况下，事务失效
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public void MultipleDataSourcesTx() {
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        User user = new User("bulkJava", dateStr);
        userService.insert(user);
        log.info("MultipleDataSourcesTx user:{}", user);

        Order order = new Order(user.getId(), "产品名" + dateStr);
        orderService.insert(order);
        log.info("MultipleDataSourcesTx order:{}", order);

        throw new RuntimeException("模拟异常");
        // 现象为：db1 库中的 user 回滚了，而db2 库中的 order 没有回滚 ！
    }

    /**
     * 测试 this 调用失效
     */
    // @Transactional
    @SuppressWarnings("all")
    public void thisTx() {
        log.info("this 调用事务失效");
        // 但是如果在 thisTx 方法上加了 Transactional 注解，那么事务就会生效，
        this.doThisTx();

        // doThisTx() 等效于 this.doThisTx();

        // 亦或者是下面这种写法也可生效
        // self.doThisTx();
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public void doThisTx() {
        log.info("this 调用事务失效");
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        User user = new User("bulkJava", dateStr);
        userService.insert(user);
        log.info("doThisTx user:{}", user);
        throw new RuntimeException("模拟异常");
        // 现象为：user 数据插入后，就算出现了异常，也没有回滚数据
    }

    /**
     * 捕获的异常不匹配导致事务不能回滚，只能捕获目标异常以及其父类。否则事务失效
     */
    @Transactional(rollbackFor = IOException.class)
    public void exceptionTx() {
        log.info("捕获的异常不匹配");
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        User user = new User("bulkJava", dateStr);
        userService.insert(user);
        log.info("exceptionTx user:{}", user);
        throw new RuntimeException("模拟异常");
        // 现象为：user 数据插入后，就算出现了异常，也没有回滚数据
    }

    @Transactional
    @SuppressWarnings("all")
    public final void finalTx() {
        log.info("final方法失效");
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        User user = new User("bulkJava", dateStr);
        // final 方法修饰 bean都注入不进来...
        userService.insert(user);
        log.info("finalTx user:{}", user);
        throw new RuntimeException("模拟异常");
    }

    @Transactional
    public void threadTx() {
        log.info("多线程事务失效");
        // new 线程，或者线程池
        new Thread(() -> {
            String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            User user = new User("bulkJava", dateStr);
            userService.insert(user);
            log.info("threadTx user:{}", user);
            throw new RuntimeException("模拟异常");
            // 现象为：user 数据插入后，就算出现了异常，也没有回滚数据
        }).start();
    }

    @Transactional(propagation = Propagation.NEVER)
    public void propagationTx() {
        log.info("事务传播行为设置错误，导致没有事务");
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        User user = new User("bulkJava", dateStr);
        userService.insert(user);
        log.info("propagationTx user:{}", user);
        throw new RuntimeException("模拟异常");
        // 现象为：user 数据插入后，就算出现了异常，也没有回滚数据
    }

    /**
     * 事务管理器设置错误，user表 应该用  db1TransactionManager
     */
    // @Transactional(transactionManager = "db1TransactionManager")
    @Transactional(transactionManager = "db2TransactionManager")
    public void transactionManagerTx() {
        log.info("事务管理器设置错误");
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        User user = new User("bulkJava", dateStr);
        userService.insert(user);
        log.info("transactionManagerTx user:{}", user);
        throw new RuntimeException("模拟异常");
        // 现象为：user 数据插入后，就算出现了异常，也没有回滚数据
    }

    /**
     * 嵌套事务 异常捕获后，仍然被回滚了
     */
    @Transactional
    public void nestedTx() {
        log.info("nestedTx 处理");
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        User user = new User("外层User", dateStr);
        userService.insert(user);
        log.info("nestedTx user:{}", user);
        try {
            userService.nestedErrTx();
        } catch (Exception e) {
            log.error("nestedTx 捕获到异常");
        }
        // 表现为，nestedErrTx 中插入的user 回滚，以及 外层nestedTx 中的 User 也被回滚了！ 原因是 spring 在事务回滚的时候，会回滚整个事务！而不是 nestedErrTx 中的指定内容
        // 还会有 异常 Transaction rolled back because it has been marked as rollback-only
        // 解决方案为 @Transactional(propagation = Propagation.REQUIRES_NEW) ，即可只回滚 nestedErrTx 中的事务，而不影响外层
    }

}
